package com.penglecode.protobuff;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.springframework.core.convert.ConversionService;

import com.penglecode.protobuff.protos.BeanMetaDataProto;

public class ProtoBufferSerializer implements BeanSerializer {

	private ConversionService conversionService;
	
	public void setConversionService(ConversionService conversionService) {
		this.conversionService = conversionService;
	}

	public <T> byte[] serialize(T target) {
		try {
			if(target == null){
				return null;
			}
			PropertyDescriptor[] pds = SerializedBeanUtils.getPropertyDescriptors(target.getClass(), Object.class);
			if(pds != null && pds.length > 0){
				BeanMetaDataProto.BeanMapper.Builder beanMapperBuilder = BeanMetaDataProto.BeanMapper.newBuilder();
				BeanMetaDataProto.BeanProperty.Builder beanPropertyBuilder = BeanMetaDataProto.BeanProperty.newBuilder();
				for(PropertyDescriptor pd : pds){
					Method readMethod = pd.getReadMethod();
					readMethod.setAccessible(true);
					Object value = readMethod.invoke(target, (Object[])null);
					if(value != null){
						beanPropertyBuilder.setName(pd.getName());
						beanPropertyBuilder.setValue(conversionService.convert(value, String.class));
						beanPropertyBuilder.setType(pd.getPropertyType().getName());
						beanMapperBuilder.addProperties(beanPropertyBuilder.build());
					}
				}
				return beanMapperBuilder.build().toByteArray();
			}
		} catch(Exception e) {
			throw new RuntimeException(e);
		}
		return null;
	}

	public <T> T deserialize(byte[] bytes, Class<T> targetType) {
		try {
			if(bytes == null){
				return null;
			}
			Map<String,PropertyDescriptor> pdMap = SerializedBeanUtils.getPropertyDescriptorMap(targetType, Object.class);
			if(pdMap != null && !pdMap.isEmpty()){
				BeanMetaDataProto.BeanMapper beanMapper = BeanMetaDataProto.BeanMapper.parseFrom(bytes);
				List<BeanMetaDataProto.BeanProperty> beanPropertyList = beanMapper.getPropertiesList();
				if(beanPropertyList != null && !beanPropertyList.isEmpty()){
					T target = SerializedBeanUtils.newInstance(targetType);
					for(BeanMetaDataProto.BeanProperty beanProperty : beanPropertyList){
						PropertyDescriptor pd = pdMap.get(beanProperty.getName());
						Method writeMethod = pd.getWriteMethod();
						writeMethod.setAccessible(true);
						Object value = conversionService.convert(beanProperty.getValue(), pd.getPropertyType());
						writeMethod.invoke(target, new Object[]{value});
					}
					return target;
				}
			}
		} catch(Exception e) {
			throw new RuntimeException(e);
		}
		return null;
	}

}